package com.ebupt.migu.music.system.controller;

import com.ebupt.migu.music.common.auth.AuthBaseUtils;
import com.ebupt.migu.music.common.auth.AuthConst;
import com.ebupt.migu.music.common.entity.ResultObject;
import com.ebupt.migu.music.common.exception.MyException;
import com.ebupt.migu.music.common.exception.StatusEnum;
import com.ebupt.migu.music.common.mapper.CommonMapper;
import com.ebupt.migu.music.common.sms.Sender;
import com.ebupt.migu.music.common.util.ReqestParamUtil;
import com.ebupt.migu.music.common.util.TokenUtils;
import com.ebupt.migu.music.common.util.VerifyCodeUtils;
import com.ebupt.migu.music.redis.service.RedisService;
import com.ebupt.migu.music.system.entity.SysMenu;
import com.ebupt.migu.music.system.entity.SysUser;
import com.ebupt.migu.music.system.service.MenuService;
import com.ebupt.migu.music.system.service.UserService;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.awt.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author zhanggk
 */
@Slf4j
@RestController
@EnableAutoConfiguration
public class LoginController {

    private static boolean isRepeat;

    @Value("${system.login.repeat}")
    public void setIsRepeat(boolean isRepeat) {
        LoginController.isRepeat = isRepeat;
    }

    @Autowired
    HttpServletRequest request;
    @Autowired
    UserService userService;
    @Autowired
    CommonMapper commonMapper;
    @Autowired
    MenuService menuService;
    @Autowired
    private RedisService redisService;
    @Resource
    private AuthBaseUtils authBaseUtils;

    private static final Pattern pattern = Pattern.compile("^(?=.*[a-zA-Z])(?=.*\\d)(?=.*[~!@#$%^&*()_+`\\-={}:\";'<>?,.\\/]).{8,20}$");

    @GetMapping("/logout")
    public void logout() {
        SysUser sysUser = AuthBaseUtils.getSessionUser(request);
        insertLoginLog(sysUser.getId(), sysUser.getLoginName(), sysUser.getUserName(), sysUser.getUserRole(), null, null, "0000", "退出系统");
        request.getSession().invalidate();
    }


    /**
     * 登陆
     *
     * @return
     */
    @RequestMapping("/check")
    public ResultObject check() {
        ResultObject<Object> resObj = new ResultObject<>();
        Map<String, Object> map = new HashMap<>(2);
        Map<String, Object> paramMap = ReqestParamUtil.getParams(request);
        String loginName = paramMap.get("loginName").toString();
        //调用方法取post请求数据
//		paramMap.putAll(postParams(request));
        String sessionId = request.getSession().getId();
        Object captchaObj = redisService.get(sessionId + AuthConst.CAPTCHA);
        //String captcha = (String) request.getSession().getAttribute(AuthConst.CAPTCHA);
        log.info("登录时候的session id:{}，验证码captcha:{}", request.getSession().getId(), captchaObj);
        // 第二部验证token
        if (paramMap.get("token") == null || StringUtils.isBlank(paramMap.get("token").toString())) {

            if (loginName == null || StringUtils.isBlank(loginName)) {
                insertLoginLog(null, null, null, null, null, null, "0007", "登录名为空");
                // 删除图形验证码
                rmCAPTCHA(sessionId);
                throw new MyException(StatusEnum.BUSINID, "账户或密码错误！");
            }

            //用户名解码
            loginName = getName(loginName);
            if (paramMap.get("captcha") == null || StringUtils.isBlank(paramMap.get("captcha").toString())) {
                insertLoginLog(null, loginName, null, null, paramMap.get("captcha").toString(), null, "0007", "图形验证码为空");
                // 删除图形验证码
                rmCAPTCHA(sessionId);
                throw new MyException(StatusEnum.BUSINID, "图形验证码为空！");
            }

            if (paramMap.get("password") == null || StringUtils.isBlank(paramMap.get("password").toString())) {
                insertLoginLog(null, loginName, null, null, paramMap.get("captcha").toString(), null, "0007", "密码为空");
                // 删除图形验证码
                rmCAPTCHA(sessionId);
                throw new MyException(StatusEnum.BUSINID, "账户或密码错误！");
            }

            SysUser sysUser = userService.getUserByLoginName(loginName);
            if (null == captchaObj) {
                insertLoginLog(sysUser.getId(), loginName, sysUser.getUserName(), sysUser.getUserRole(), paramMap.get("captcha").toString(), null, "1006", "图形验证码错误");
                // 删除图形验证码
                rmCAPTCHA(sessionId);
                throw new MyException(StatusEnum.CAPTCHA_ERR, "图形验证码错误！");
            }

            String captcha = String.valueOf(captchaObj);
            if (null == captcha || !captcha.equalsIgnoreCase(paramMap.get("captcha").toString())) {
                insertLoginLog(sysUser.getId(), loginName, sysUser.getUserName(), sysUser.getUserRole(), paramMap.get("captcha").toString(), null, "1006", "图形验证码错误");
                // 删除图形验证码
                rmCAPTCHA(sessionId);
                throw new MyException(StatusEnum.CAPTCHA_ERR, "图形验证码错误！");
            }

            if (sysUser == null) {
                insertLoginLog(null, loginName, null, null, paramMap.get("captcha").toString(), null, "0005", "账户或密码错误");
                // 删除图形验证码
                rmCAPTCHA(sessionId);
                throw new MyException(StatusEnum.USERNAME_PASSWORD_ERR, "账户或密码错误！");
            }

            // 密码解码
            String password = getPass(paramMap.get("password").toString());
            if (!sysUser.getPassword().equals(password)) {
                insertLoginLog(null, loginName, null, null, paramMap.get("captcha").toString(), null, "0005", "账户或密码错误");
                // 删除图形验证码
                rmCAPTCHA(sessionId);
                throw new MyException(StatusEnum.USERNAME_PASSWORD_ERR, "账户或密码错误！");
            }

            // 密码校验通过，但密不符合规范，要求强制修改密码
            if (sysUser.getPassword().equals(password) && "1".equals(String.valueOf(paramMap.get("ver")))) {
                // insertLoginLog(sysUser.getId(), paramMap.get("loginName").toString(), sysUser.getUserName(), sysUser.getUserRole(), paramMap.get("captcha").toString(), null, "1006", "图形验证码错误");
                map.put("userId", sysUser.getId());
                map.put("pwd", sysUser.getPassword());
                resObj.setCode(StatusEnum.PASS_VER_FAIL.getCode());
                resObj.setMsg(StatusEnum.PASS_VER_FAIL.getMsg());
                resObj.setData(map);
                return resObj;
            }
            //生成短信验证码
            String smsCode = VerifyCodeUtils.generateVerifyCode(6, "0123456789");
            log.info("短信验证码：" + smsCode);
            // 发送短信
            Sender.sendSMS(
                    Sender.LOGIN_CODE,
                    new String[]{sysUser.getMobile()},
                    new String[]{smsCode}
            );
            // 设置短信验证码

            //request.getSession().setAttribute(AuthConst.SMS_CODE, smsCode);
            redisService.set(sessionId + AuthConst.SMS_CODE, smsCode, 61L);
            // 设置用户session
            request.getSession().setAttribute(AuthConst.USER_BASE_INFO, sysUser);
            // 删除图形验证码
            rmCAPTCHA(sessionId);
            //request.getSession().removeAttribute(AuthConst.CAPTCHA);
            //redisService.deleteKeyByKey(sessionId+AuthConst.CAPTCHA);
//            request.getSession().setAttribute(AuthConst.CAPTCHA, VerifyCodeUtils.generateVerifyCode(4));
            // 生成 token
            // request.getSession().setAttribute(AuthConst.TOKEN, TokenUtils.createJwtToken(paramMap.get("loginName").toString()));

            map.put("token", TokenUtils.createJwtToken(loginName));
            resObj.setData(map);
            insertLoginLog(sysUser.getId(), loginName, sysUser.getUserName(), sysUser.getUserRole(), paramMap.get("captcha").toString(), smsCode, "0000", "下发短信验证码");
        } else {
            SysUser sysUser = AuthBaseUtils.getSessionUser(request);
            Object smsCodeObj = redisService.get(sessionId + AuthConst.SMS_CODE);
            String smsCode = "";//(String) request.getSession().getAttribute(AuthConst.SMS_CODE);
            if (smsCodeObj != null) {
                smsCode = String.valueOf(smsCodeObj);
            }
            try {
                TokenUtils.parseJWT(paramMap.get("token").toString());
            } catch (Exception e) {
                throw new MyException(StatusEnum.TOKEN_INVALID, "token失效");
            }
            // 正式环境需要验证smsCode，测试环境不需要验证
            if (isRepeat) {
                if (!smsCode.equals(paramMap.get("smsCode").toString())) {
                    insertLoginLog(sysUser.getId(), loginName, sysUser.getUserName(), sysUser.getUserRole(), paramMap.get("captcha").toString(), paramMap.get("smsCode").toString(), "1007", "短信验证码错误");
                    throw new MyException(StatusEnum.LOGIN, "短信验证码错误！");
                } else {
                    insertLoginLog(sysUser.getId(), loginName, sysUser.getUserName(), sysUser.getUserRole(), paramMap.get("captcha").toString(), paramMap.get("smsCode").toString(), "0000", "登录成功");
                    map.put("sysUser", sysUser);
                    map.put("menuTree", menuService.getMenuByUserId(sysUser.getId()));
                    // 验证通过后删除短信验证码
                    // request.getSession().removeAttribute(AuthConst.SMS_CODE);
                    redisService.deleteKeyByKey(sessionId + AuthConst.SMS_CODE);
                    resObj.setData(map);
                    return resObj;
                }
            } else {
                insertLoginLog(sysUser.getId(), loginName, sysUser.getUserName(), sysUser.getUserRole(), paramMap.get("captcha").toString(), paramMap.get("smsCode").toString(), "0000", "登录成功");
                map.put("sysUser", sysUser);
                map.put("menuTree", menuService.getMenuByUserId(sysUser.getId()));
                resObj.setData(map);
                // 验证通过后删除短信验证码
                // request.getSession().removeAttribute(AuthConst.SMS_CODE);
                return resObj;
            }
        }
        return resObj;
    }

    /**
     * 密码解密
     *
     * @return
     */
    public String getPass(String pass) {
        String substring_1 = pass.substring(0, 10);
        String substring_2 = pass.substring(15, 20);
        String substring_3 = pass.substring(25, 42);
        return substring_1 + substring_2 + substring_3;
    }

    /**
     * 用户户名解码
     *
     * @return
     */
    public String getName(String name) {
        String loginName;
        //String  substring_1= name.substring(0, 10);
        //String  substring_2= name.substring(15, 20);
        //String  substring_3= name.substring(25, 42);
        try {
            // 解码
            byte[] base64decodedBytes = Base64.getDecoder().decode(name.substring(10));
            loginName = new String(base64decodedBytes, "utf-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        return loginName;
    }

    /**
     * 修改密码
     *
     * @return
     */
    @RequestMapping("/modify")
    @ResponseBody
    public ResultObject<Object> modify() {
        ResultObject<Object> retObj = new ResultObject<Object>();
        Map<String, Object> retMap = new HashMap<>();
        Map<String, Object> paramMap = ReqestParamUtil.getParams(request);

        String userId = userService.modifyUser(paramMap);

        retMap.put("userId", userId);
        retObj.setMsg("修改用户成功");
        retObj.setData(retMap);

        return retObj;
    }

    /**
     * @return
     */
    @GetMapping("/reSendSMS")
    public ResultObject<String> reSendSMS() {
        ResultObject<String> resObj = new ResultObject<>();
        Map<String, Object> paramMap = ReqestParamUtil.getParams(request);
        try {
            TokenUtils.parseJWT(paramMap.get("token").toString());
        } catch (Exception e) {
            throw new MyException(StatusEnum.TOKEN_INVALID, "token失效");
        }
        SysUser sysUser = AuthBaseUtils.getSessionUser(request);
        String smsCode = (String) request.getSession().getAttribute(AuthConst.SMS_CODE);
        Sender.sendSMS(
                Sender.LOGIN_CODE,
                new String[]{sysUser.getMobile()},
                new String[]{smsCode}
        );
        resObj.setMsg("短信发送成功。");
        return resObj;
    }

    /**
     * 生成图形验证码
     *
     * @return
     * @throws Exception
     */
    @GetMapping("/captcha")
    public ResultObject<String> captcha() throws Exception {

        ResultObject<String> resObj = new ResultObject<>();
        String captcha = VerifyCodeUtils.generateVerifyCode(4);
        request.getSession().setAttribute(AuthConst.CAPTCHA, captcha);
        log.info("获取验证码时候的session id:{}", request.getSession().getId());
        redisService.set(request.getSession().getId() + AuthConst.CAPTCHA, captcha, 180L);
        log.info("图形验证码：" + captcha);
        String base64Img = "data:image/png;base64," + VerifyCodeUtils.createImageBASE64(captcha, new Font("微软雅黑", Font.PLAIN, 20), 75, 40);
        resObj.setData(base64Img);
        return resObj;
    }

//    /**
//     * 校验图形验证码
//     *
//     * @return
//     * @throws Exception
//     */
//    @GetMapping("/verifyCode")
//    public ResultObject<String> verifyCode(String captcha) throws Exception {
//        String isEquals = "false";
//        String redisCode = "";
//        ResultObject<String> resObj = new ResultObject<>();
//        log.info("获取校验验证码时候的session id:{}", request.getSession().getId());
//        Object redisCap = redisService.get(request.getSession().getId()+AuthConst.CAPTCHA);
//        log.info("前端图形验证码：" + captcha + "；redis图形验证码：" + redisCap);
//        if(redisCap != null){
//            redisCode = redisCap.toString();
//            isEquals = redisCode.equalsIgnoreCase(captcha) ? "true" : isEquals;
//            resObj.setData(isEquals);
//        } else {
//            resObj.setData("验证码已失效，请刷新页面重新验证");
//        }
//        return resObj;
//    }

    /**
     * 方舟账号登录服务云
     * <br>
     * 要求：不需要账号、验证码，只需要手机号
     *
     * @param mobile 手机号
     * @return token等参数
     */
    @PostMapping("/dataArkLogin")
    public ResultObject<Map<String, Object>> dataArkLogin(String mobile) {
        log.info("手机号码：{}", mobile.substring(8, 19));
        SysUser sysUser = userService.getUserByMobile(mobile.substring(8, 19));
        if (sysUser == null) {
            throw new MyException(StatusEnum.USER_VERIFICATION_FAILED, "用户不存在");
        }
        // 设置用户session
        request.getSession().setAttribute(AuthConst.USER_BASE_INFO, sysUser);
        // 生成token返回前端
        String token = TokenUtils.createJwtToken(sysUser.getLoginName());
        Map<String, Object> map = new HashMap<>(1);
        map.put("token", token);
        try {
            insertLoginLog(sysUser.getId(), sysUser.getLoginName(), sysUser.getUserName(), null, null, null, "0000", "方舟跳转登录成功，登录成功");
        } catch (Exception e) {
            log.info("方舟账号登录服务云,日志保存失败");
            throw new MyException(StatusEnum.ERROR, "登录日志记录失败");
        }
        return new ResultObject<>("0000", "查询成功", map);
    }


    /**
     * 【方舟账号专用】获取账号的菜单
     * <br>
     * 要求：不需要短信验证
     *
     * @return 菜单栏和用户信息
     */
    @PostMapping("/arkAccountMenu")
    public ResultObject<Map<String, Object>> arkAccountMenu() {
        // 获取用户信息
        SysUser sysUser = authBaseUtils.getUserByToken(request);
        // 获取用户菜单信息
        List<SysMenu> menuTree = menuService.getMenuByUserId(sysUser.getId());
        // 将用户信息和菜单信息放入集合中返回
        Map<String, Object> map = new HashMap<>(2);
        map.put("sysUser", sysUser);
        map.put("menuTree", menuTree);
        return new ResultObject<>("0000", "获取菜单成功", map);
    }


    /**
     * 软小康某平台方舟账号嵌套
     * <br>
     * 要求：不需要账号、验证码，只需要手机号
     *
     * @return token等参数
     */
    @PostMapping("/rxkLogin")
    public ResultObject<Map<String, Object>> rxkLogin() {
        String mobile = request.getHeader("msisdn");
        log.info("电话号码 ：{}", mobile);
        SysUser sysUser = userService.getUserByMobile(mobile);
        if (sysUser == null) {
            throw new MyException(StatusEnum.USER_VERIFICATION_FAILED, "用户不存在");
        }
        // 设置用户session
        request.getSession().setAttribute(AuthConst.USER_BASE_INFO, sysUser);
        // 生成token返回前端
        String token = TokenUtils.createJwtToken(sysUser.getLoginName());
        Map<String, Object> map = new HashMap<>(1);
        map.put("token", token);
        try {
            insertLoginLog(sysUser.getId(), sysUser.getLoginName(), sysUser.getUserName(), null, null, null, "0000", "方舟跳转登录成功，登录成功");
        } catch (Exception e) {
            log.info("方舟账号登录服务云,日志保存失败");
            throw new MyException(StatusEnum.ERROR, "登录日志记录失败");
        }
        return new ResultObject<>("0000", "查询成功", map);
    }


    /**
     * 【软小康方舟账号专用】获取账号的菜单
     * <br>
     * 要求：不需要短信验证
     *
     * @return 菜单栏和用户信息
     */
    @PostMapping("/rxkAccountMenu")
    public ResultObject<Map<String, Object>> rxkAccountMenu() {
        // 获取用户信息
        SysUser sysUser = authBaseUtils.getUserByToken(request);
        // 获取用户菜单信息
        List<SysMenu> menuTree = menuService.getMenuByUserId(sysUser.getId());
        // 将用户信息和菜单信息放入集合中返回
        Map<String, Object> map = new HashMap<>(2);
        map.put("sysUser", sysUser);
        map.put("menuTree", menuTree);
        return new ResultObject<>("0000", "获取菜单成功", map);
    }


    /**
     * 登陆日志
     *
     * @param userId
     * @param loginName
     * @param userName
     * @param menuId
     * @param captcha
     * @param smsCode
     * @param loginCode
     * @param loginResult
     */
    private void insertLoginLog(Integer userId, String loginName, String userName, String menuId, String captcha, String smsCode, String loginCode, String loginResult) {
        Map<String, Object> paramMap = new HashMap<>(9);
        paramMap.put("sessionId", request.getSession().getId());
        paramMap.put("userId", userId);
        paramMap.put("loginName", loginName);
        paramMap.put("userName", userName);
        paramMap.put("menuId", menuId);
        paramMap.put("loginCode", loginCode);
        paramMap.put("loginResult", loginResult);
        paramMap.put("smsCode", smsCode);
        paramMap.put("captcha", captcha);
        commonMapper.insertLoginLog(paramMap);
    }

    /**
     * 尝试从post读取参数
     */
    private static Map<String, Object> postParams(HttpServletRequest request) {
        Map<String, Object> paramMap = new HashMap<>();
        StringBuilder stringBuilder = new StringBuilder();
        try (BufferedReader reader = request.getReader()) {
            char[] chars = new char[1024];
            int len;
            while ((len = reader.read(chars)) != -1) {
                stringBuilder.append(chars, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        Gson gson = new Gson();
        Map<String, Object> map = gson.fromJson(stringBuilder.toString(), Map.class);
        paramMap.putAll(map);
        return paramMap;
    }

    /**
     * 对密码进行格式校验
     *
     * @param pwd
     * @return
     */
    public static boolean isPWD(String pwd) {
        Matcher matcher = pattern.matcher(pwd);
        return matcher.matches();
    }


    /**
     * 删除图形验证码
     *
     * @param sessionId
     * @return
     */
    public void rmCAPTCHA(String sessionId) {
        request.getSession().removeAttribute(AuthConst.CAPTCHA);
        redisService.deleteKeyByKey(sessionId + AuthConst.CAPTCHA);
    }

    /**
     * 为了防止序列化不一致，删除redis中的springSession
     *
     * @return 操作结果
     */
    @GetMapping("/deleteRedisSpringKey")
    public ResultObject<String> deleteSpringKey() {
        String key = "spring:*";
        redisService.deleteKeys(key);
        return new ResultObject<>();
    }
}